/* ========================================================================= */
/* ------------------------------------------------------------------------- */
/*!
  \file			ETSubItem.cc
  \date			september 2011
  \author		TNick

  \brief		Contains the implementation for ETSubItem class


  Copyright (c) 2011, Nicu Tofan
 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:
 * Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.
 * Neither the name of the copyright holder nor the
   names of its contributors may be used to endorse or promote products
   derived from this software without specific prior written permission.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS" AND
 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 DISCLAIMED. IN NO EVENT SHALL NICU TOFAN BE LIABLE FOR ANY
 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

*/
/* ------------------------------------------------------------------------- */
/* ========================================================================= */
//
//
//
//
/*  INCLUDES    ------------------------------------------------------------ */

#include	<main.h>

#include	<sheet/ETSheetView.h>
#include	<sheet/ETSheet.h>
#include	<sheet/ETI_CplxTxt.h>

#include	<sheet/subitems/ETSI_Fract.h>
#include	<sheet/subitems/ETSI_HText.h>
#include	<sheet/subitems/ETSI_Integ.h>
#include	<sheet/subitems/ETSI_Lim.h>
#include	<sheet/subitems/ETSI_LogicAc.h>
#include	<sheet/subitems/ETSI_Matrix.h>
#include	<sheet/subitems/ETSI_Radic.h>
#include	<sheet/subitems/ETSI_SText.h>
#include	<sheet/subitems/ETSI_Sum.h>
#include	<sheet/subitems/ETSI_Root.h>

#include	"ETSubItem.h"

/*  INCLUDES    ============================================================ */
//
//
//
//
/*  CLASS    --------------------------------------------------------------- */

/* ------------------------------------------------------------------------- */
ETSubItem::ETSubItem	( void )
	: CC_L2D ()
{
	/* itm_font initialises itself at creation time */
	itm_col = QColor(Qt::white);
	itm_pen = 0;
	bbox = QRectF(0, 0, 0, 0);
	flg = DEFAULT_STATE;
	fth.fth_cplx = NULL;
	x_pos = 0;
}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
ETSubItem::~ETSubItem	( void )
{
}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
ETI_CplxTxt *		ETSubItem::fatherCplx		( void ) const
{
	const ETSubItem * itr_1 = this;

	while (!itr_1->isTopLevel())
	{
		itr_1 = ((ETSubItem *)itr_1)->fth.fth_sub_i;
		if (itr_1 == NULL)
			return NULL;
	}

	return itr_1->fth.fth_cplx;

}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
void			ETSubItem::setSizeDirty		(ETI_CplxTxt ** tp_fth)
{
	flg = (Flags)(flg | SIZE_DIRTY);

	/* iterate in all parents */
	ETSubItem * iter_i = fatherSubI();
	ETSubItem * iter_j = this;
	while (iter_i != NULL)
	{
		iter_i->setKidSizeDirty();
		iter_j = iter_i;
		iter_i = iter_i->fatherSubI();
	}
	if (tp_fth != NULL)
	{
		*tp_fth = iter_j->fth.fth_cplx;
	}
}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
void			ETSubItem::setFont			(QFont new_font)
{
	itm_font = new_font;
	setSizeDirty();

	if (!isTopLevel())
	{
		if (fth.fth_sub_i->font() == new_font)
		{
			flg = (Flags)(flg & (~FONT_MOD));
			return;
		}
	}
	flg = (Flags)(flg | FONT_MOD);
}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
void			ETSubItem::setColor			(QColor new_color)
{
	itm_col = new_color;

	if (!isTopLevel())
	{
		if (fth.fth_sub_i->color() == new_color)
		{
			flg = (Flags)(flg & (~COL_MOD));
			return;
		}
	}
	flg = (Flags)(flg | COL_MOD);

}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
void			ETSubItem::setPen			(QRgb new_color)
{
	itm_pen = new_color;

	if (!isTopLevel())
	{
		if (fth.fth_sub_i->pen() == new_color)
		{
			flg = (Flags)(flg & (~PEN_MOD));
			return;
		}
	}
	flg = (Flags)(flg | PEN_MOD);

}
/* ========================================================================= */



/* ------------------------------------------------------------------------- */
void			ETSubItem::prePaint		(
		PaintData * pd)
{

	/* get the bounding box in painter's coordinate system */
	pd->t_box = bbox;
	pd->t_box.translate(pd->pt_ref);
	pd->painter->save();

	/* replace the font if that is needed */
	if (!inheritsFont())
	{
		pd->painter->setFont(itm_font);
	}

	if (isSelected())
	{
		pd->painter->setBackground(QBrush(Qt::lightGray));
		pd->painter->fillRect(pd->t_box, QBrush(Qt::lightGray));
		pd->painter->setPen(Qt::black);
	}
	else
	{
		/* replace the color if that is needed */
		if (!inheritsColor())
		{
			pd->painter->setBackground(QBrush(itm_col));
			pd->painter->fillRect(pd->t_box, QBrush(itm_col));
		}

		/* replace the pen if that is needed */
		if (!inheritsPen())
		{
			pd->painter->setPen(QColor(itm_pen));
		}
	}
}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
void			ETSubItem::postPaint		(
		PaintData * pd)
{

	/* mark current item */
	if (pd->vp->currentSubI() == this)
	{
		qreal pen_sz;
		if (pd->vp->getScale() < 1.44)
		{
			pen_sz = 0;
		}
		else
		{
			pen_sz = 0.7;
		}
		const QColor bgcolor( // ensure good contrast against fgcolor
							  itm_col.red()   > 127 ? 0 : 255,
							  itm_col.green() > 127 ? 0 : 255,
							  itm_col.blue()  > 127 ? 0 : 255);

		pd->painter->setPen(QPen(bgcolor, pen_sz, Qt::DotLine));

		pd->painter->drawLine(
					pd->t_box.bottomLeft(),
					pd->t_box.bottomRight());
		if (pd->vp->selIsLeft())
		{
			pd->painter->drawLine(
						pd->t_box.bottomLeft(),
						pd->t_box.topLeft());
		}
		else
		{
			pd->painter->drawLine(
						pd->t_box.bottomRight(),
						pd->t_box.topRight());
		}
	}
	else
	{ /// @todo use code below only as needed
//		QPen pn_t = pd->painter->pen();
//		QPen new_p;
//		new_p.setColor(QColor(Qt::black));
//		new_p.setCosmetic(true);
//		new_p.setCapStyle(Qt::RoundCap);
//		new_p.setStyle(Qt::DotLine);
//		new_p.setJoinStyle(Qt::RoundJoin);
//		new_p.setWidth(1);

//		pd->painter->setPen(new_p);

//		pd->painter->drawRect(pd->t_box);

//		pd->painter->setPen(pn_t);
	}

	pd->painter->restore();

	return;
}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
void			ETSubItem::navLeaveTop			(
		ETI_CplxTxt * fth, ETSheetView * vp )
{

	if (isTopLevel())
	{	/* this is outside our scope.  */
		/* The viewport shall select another item */

		vp->navLeaveTop(fatherCplx());
		return;
	}

	/* if the father has another kid above this one, select that */
	ETSubItem * parent_si = fatherSubI();
	if (parent_si->kidNavReq_up(vp, this))
	{
		return;
	}

	/* default to selecting the parent */
	vp->setCurrentSubI(fth, parent_si);

	/// @todo Option to skip the parent (CTRL, maybe) and go to item above

}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
void			ETSubItem::navLeaveBtm			(
		ETI_CplxTxt * fth, ETSheetView * vp )
{

	if (isTopLevel())
	{	/* this is outside our scope.  */
		/* The viewport shall select another item */

		vp->navLeaveBtm(fatherCplx());
		return;
	}

	/* if the father has another kid above this one, select that */
	ETSubItem * parent_si = fatherSubI();
	if (parent_si->kidNavReq_down(vp, this))
	{
		return;
	}

	/* default to selecting the parent */
	vp->setCurrentSubI(fth, parent_si);

	/// @todo Option to skip the parent (CTRL, maybe) and go to item below
}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
void			ETSubItem::navLeaveLeft			(
		ETI_CplxTxt * fth, ETSheetView * vp )
{

	/* select previous item in this chain, if available */
	ETSubItem * sit_sib = prev();
	if (sit_sib != NULL)
	{
		vp->setCurrentSubI(fth, sit_sib);
		vp->setSelLeft();
		return;
	}

	if (isTopLevel())
	{	/* this is outside our scope.  */
		/* The viewport shall select another item */

		vp->navLeaveLeft(fatherCplx());
		return;
	}

	/* select the parent on the left side */
	vp->setCurrentSubI(fth, fatherSubI());
	vp->setSelLeft();

}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
void			ETSubItem::navLeaveRight		(
		ETI_CplxTxt * fth, ETSheetView * vp )
{
	/* select next item in this chain, if available */
	ETSubItem * sit_sib = next();
	if (sit_sib != NULL)
	{
		vp->setCurrentSubI(fth, sit_sib);
		vp->setSelRight();
		return;
	}

	if (isTopLevel())
	{	/* this is outside our scope.  */
		/* The viewport shall select another item */

		vp->navLeaveRight(fatherCplx());
		return;
	}

	/* select the parent on the left side */
	vp->setCurrentSubI(fth, fatherSubI());
	vp->setSelRight();

}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
void			ETSubItem::navKey		(
		ETSheetView * vp, Qt::Key key)
{
	/* it is implied that this is the current item in the viewport */
	Q_ASSERT(vp->currentSubI() == this);

	switch (key)		{
	case	Qt::Key_Up:			{
			navLeaveTop(vp->currentItem(), vp);
			break;}
	case	Qt::Key_Down:		{
			navLeaveBtm(vp->currentItem(), vp);
			break;}
	case	Qt::Key_Left:		{
			if (vp->selIsLeft())
				{
					navLeaveLeft(vp->currentItem(), vp);
				}
			else
				{
					vp->setSelLeft();
				}
			break;}
	case	Qt::Key_Right:		{
			if (vp->selIsLeft())
				{
					vp->setSelRight();
				}
			else
				{
					navLeaveRight(vp->currentItem(), vp);
				}
			break;}
	default:					{
		qDebug() << "ETSubItem::navKey finds the value "
				 << key << " inappropriate.";
		break;}
	}

	/* this may not be the best approach, as it tiggers a redraw in all
	  viewports and it redraws entire item while it might be that only a
	  part must be redrawn */
	vp->currentItem()->update();

}
/* ========================================================================= */


/* ------------------------------------------------------------------------- */
QFont			ETSubItem::font			( void ) const
{
	if (!inheritsFont())
	{
		return itm_font;
	}
	const ETSubItem* itr_i = this;

	while (!itr_i->isTopLevel())
	{
		if (!itr_i->inheritsFont())
		{
			return itr_i->itm_font;
		}
		itr_i = itr_i->fatherSubI();
	}
	if (!itr_i->inheritsFont())
	{
		return itr_i->itm_font;
	}
	return QFont();
}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
void			ETSubItem::remove			( bool b_recomp )
{
	ETI_CplxTxt * top_it;

	/* mark the item "dirty" */
	setSizeDirty(&top_it);

	/// @todo implement undo function here; instead of deleting, add it to a queue

	/* top level items can't be destroyed, as they are being embedded */

	LIST_BUG "the item to remove: " << this
									<< " the parent: ";
	top_it->dbgDump();


	if (!isTopLevel())
	{
		LIST_BUG "not top level";

		/* make shure that no viewport uses this pointer */
		foreach	(QGraphicsView * vp, top_it->scene()->views())
		{
			/* we are assuming that all views are ETSheetView !*/
			ETSheetView * vp_sv = static_cast<ETSheetView *>(vp);

			if (vp_sv->currentSubI() == this)
			{
				if (vp_sv->selIsLeft())
				{
					if (next() != NULL)
						vp_sv->setCurrentSubI(top_it, next());
					else if (prev() != NULL)
						vp_sv->setCurrentSubI(top_it, prev());
					else
						vp_sv->setCurrentSubI(top_it, fatherSubI());
				}
				else
				{
					if (prev() != NULL)
						vp_sv->setCurrentSubI(top_it, prev());
					else if (next() != NULL)
						vp_sv->setCurrentSubI(top_it, next());
					else
						vp_sv->setCurrentSubI(top_it, fatherSubI());
				}

			}
		}
		LIST_BUG "the item to remove: ";
		dbgDump();
		fatherSubI()->ackRemoveKid(this);

		/* check for top level elements that contain only one item */
		if (fatherSubI()->isTopLevel())
		{
			LIST_BUG "father is top level";
			if (top_it->count() == 0)
			{
				LIST_BUG "count is 0 => reinit";
				top_it->reinit(false);
			}
		}

		delete this;
	}
	else
	{
		LIST_BUG "top level";
		top_it->reinit(false);
	}

	/* recompute the item */
	if (b_recomp == true)
	{
		top_it->recompute(true);
	}

}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
void			ETSubItem::deselect				(
		ETI_CplxTxt ** fth_, bool	 b_redraw )
{
	if (!isSelected())
		return;

	flg = (Flags)(flg & (~SELECTED));

	if (b_redraw)
	{
		ETI_CplxTxt * fth_loc;
		if (fth_ == NULL)
		{
			fth_loc = fatherCplx();
		}
		else if (*fth_ == NULL)
		{
			fth_loc = fatherCplx();
			*fth_ = fth_loc;
		}
		else
		{
			fth_loc = *fth_;
		}
		fth_loc->update();
	}
}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
void			ETSubItem::select				(
		ETI_CplxTxt ** fth_, bool	 b_redraw )
{
	flg = (Flags)(flg | SELECTED);

	if (b_redraw)
	{
		ETI_CplxTxt * fth_loc;
		if (fth_ == NULL)
		{
			fth_loc = fatherCplx();
		}
		else if (*fth_ == NULL)
		{
			fth_loc = fatherCplx();
			*fth_ = fth_loc;
		}
		else
		{
			fth_loc = *fth_;
		}
		fth_loc->update();
	}

}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
QRectF			ETSubItem::boxInItem	(ETI_CplxTxt ** fth) const
{
	const ETSubItem * itr_1 = this->fatherSubI();
	const ETSubItem * itr_2 = this;
	QRectF rct_t = bbox;

	while (itr_1 != NULL)
	{
		rct_t.translate(itr_1->subIRefPt(itr_2));

		itr_2 = itr_1;
		itr_1 = ((ETSubItem *)itr_1)->fatherSubI();
	}
	/* itr_2 now has the root item inside ETI_CplxTxt */
	if (fth != NULL)
	{
		*fth = itr_2->fth.fth_cplx;
	}
	return rct_t;

}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
QRectF			ETSubItem::boxInScene	(ETI_CplxTxt ** fth) const
{
	ETI_CplxTxt * fth_loc;
	QRectF rct_t = boxInItem(&fth_loc);
	rct_t.translate(fth_loc->pos());
	if (fth != NULL)
	{
		*fth = fth_loc;
	}
	return rct_t;
}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
QPointF			ETSubItem::subIRefPt		(const ETSubItem * p_kid) const
{
	return QPointF(p_kid->posX(), 0);
}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
void			ETSubItem::loadXMLCmn			(
		ETSubItem * item, QDomElement dom_e )
{
	QDomElement dom_it;

	/* if the font entry exists then the item has a different font than
it's parent */
	dom_it = dom_e.firstChildElement(XML_TAG_FONT);
	if (!dom_it.isNull())
	{
		if (!item->itm_font.fromString(dom_it.text()))
		{
			qDebug("Font was specified for sub-item but "
				   "the content could not be parsed ");
		}
		item->flg = (Flags)(item->flg | FONT_MOD);
		if (!dom_it.nextSiblingElement(XML_TAG_FONT).isNull())
		{
			qDebug() << "The input presented more than one "
					 << XML_TAG_FONT << " tags, when only one per "
					 << "sub-item element is allowed";
		}
	}

	/* if the color entry exists then the item has a different color than
it's parent */
	dom_it = dom_e.firstChildElement(XML_TAG_COLOR);
	if (!dom_it.isNull())
	{
		item->itm_col.setNamedColor(dom_it.text());
		item->flg = (Flags)(item->flg | COL_MOD);

		if (!dom_it.nextSiblingElement(XML_TAG_COLOR).isNull())
		{
			qDebug() << "The input presented more than one "
					 << XML_TAG_COLOR << " tags, when only one per "
					 << "sub-item element is allowed";
		}
	}


	/* if the pen entry exists then the item has a different pen than
it's parent */
	dom_it = dom_e.firstChildElement(XML_TAG_PEN);
	if (!dom_it.isNull())
	{
		bool b_ok = false;
		item->itm_pen = dom_it.text().toUInt(&b_ok);
		if (!b_ok)
		{
			qDebug("Pen was specified for sub-item but "
				   "the content could not be parsed ");
		}
		item->flg = (Flags)(item->flg | PEN_MOD);
		if (!dom_it.nextSiblingElement(XML_TAG_PEN).isNull())
		{
			qDebug() << "The input presented more than one "
					 << XML_TAG_PEN << " tags, when only one per "
					 << "sub-item element is allowed";
		}
	}


}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
ETSubItem *		ETSubItem::loadXMLByType	( QDomElement dom_e )
{
	ETSubItem * ret_i = NULL;
	QDomElement dom_it;

	/// @todo	code for sub-item types as they are being created
	/* the one and only common data to all kinds of sub-items */
	dom_it = dom_e.firstChildElement(XML_TAG_TYPE);
	if (!dom_it.isNull())
	{
		QString s_ty = dom_it.text();
		if (s_ty == XML_SIT_FRACT)
		{
			// ETSI_Fract * ret_spc = new ETSI_Fract();
			// ret_i = ret_spc;
			// ret_spc->loadXML(dom_e);
		}
		else if (s_ty == XML_SIT_HTEXT)
		{
			// ETSI_HText * ret_spc = new ETSI_HText();
			// ret_i = ret_spc;
			// ret_spc->loadXML(dom_e);
		}
		else if (s_ty == XML_SIT_INTEG)
		{
			// ETSI_Integ * ret_spc = new ETSI_Integ();
			// ret_i = ret_spc;
			// ret_spc->loadXML(dom_e);
		}
		else if (s_ty == XML_SIT_LIM)
		{
			// ETSI_Lim * ret_spc = new ETSI_Lim();
			// ret_i = ret_spc;
			// ret_spc->loadXML(dom_e);
		}
		else if (s_ty == XML_SIT_LOGICAC)
		{
			// ETSI_LogicAc * ret_spc = new ETSI_LogicAc();
			// ret_i = ret_spc;
			// ret_spc->loadXML(dom_e);
		}
		else if (s_ty == XML_SIT_MATRIX)
		{
			// ETSI_Matrix * ret_spc = new ETSI_Matrix();
			// ret_i = ret_spc;
			// ret_spc->loadXML(dom_e);
		}
		else if (s_ty == XML_SIT_RADIC)
		{
			// ETSI_Radic * ret_spc = new ETSI_Radic();
			// ret_i = ret_spc;
			// ret_spc->loadXML(dom_e);
		}
		else if (s_ty == XML_SIT_STEXT)
		{
			ret_i = ETSI_SText::loadXML(dom_e);
		}
		else if (s_ty == XML_SIT_SUM)
		{
			// ETSI_Sum * ret_spc = new ETSI_Sum();
			// ret_i = ret_spc;
			// ret_spc->loadXML(dom_e);
		}
		else if (s_ty == XML_SIT_ROOT)
		{
			ret_i = ETSI_Root::loadXML(dom_e);
		}
		else
		{
			qDebug() << "ETSubItem::loadXMLByType did not create an "
					 << "sub-item because provided type ("
					 << s_ty << ") did match none of the known types.";
			return NULL;
		}


		if (!dom_it.nextSiblingElement(XML_TAG_TYPE).isNull())
		{
			qDebug() << "The input presented more than one "
					 << XML_TAG_TYPE << " tags, when only one per "
					 << "sub-item element is allowed";
		}
	}
	else
	{
		qDebug() << "ETSubItem::loadXMLByType did not create an "
				 << "sub-item because no " << XML_TAG_TYPE << " tag "
				 << " was found. ";
		return NULL;
	}

	return ret_i;
}
/* ========================================================================= */


/* ------------------------------------------------------------------------- */
void		ETSubItem::saveXMLCmn		(
	QDomElement dom_e)
{

	QDomElement dom_it;
	QDomText dom_tx;

	/* save font */
	if ((isTopLevel()) || (!inheritsFont()))
		{
			dom_it = dom_e.ownerDocument().createElement(XML_TAG_FONT);
			dom_e.appendChild(dom_it);
			dom_tx = dom_e.ownerDocument().createTextNode(itm_font.toString());
			dom_it.appendChild(dom_tx);
		}


	/* save color */
	if ((isTopLevel()) || (!inheritsColor()))
		{
			dom_it = dom_e.ownerDocument().createElement(XML_TAG_COLOR);
			dom_e.appendChild(dom_it);
			dom_tx = dom_e.ownerDocument().createTextNode(itm_col.name());
			dom_it.appendChild(dom_tx);
		}


	/* save pen */
	if ((isTopLevel()) || (!inheritsPen()))
		{
			dom_it = dom_e.ownerDocument().createElement(XML_TAG_PEN);
			dom_e.appendChild(dom_it);
			dom_tx = dom_e.ownerDocument()
					.createTextNode(QString("%1").arg(itm_pen));
			dom_it.appendChild(dom_tx);
		}


}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
void			ETSubItem::makeCurrent			(ETSheetView * vp)
{
	vp->makeCurrentSubI(NULL, this);
}
/* ========================================================================= */


/*  CLASS    =============================================================== */
//
//
//
//
/* ------------------------------------------------------------------------- */
/* ========================================================================= */
